import os, sys, subprocess, time, json, threading
from pathlib import Path
from functools import lru_cache
import pystray
from PIL import Image
import pymysql
import subprocess
import os
import tempfile, textwrap
import ctypes
import time
# ---------- 路径 ----------
DETACHED_FLAGS = (
    subprocess.CREATE_NEW_PROCESS_GROUP
    | subprocess.DETACHED_PROCESS
    | subprocess.CREATE_NO_WINDOW
)
@lru_cache
def resource_path(rel: str) -> str:
    base = Path(sys._MEIPASS).parent if hasattr(sys, "_MEIPASS") else Path(__file__).resolve().parent
    return str((base / rel).resolve())

# ---------- 配置 ----------
CFG_FILE = resource_path("mysql_cfg.json")
MYSQL_DIR = resource_path("mysql")
BASE_DIR = Path(MYSQL_DIR)
DATA_DIR = BASE_DIR / "data"
BIN_DIR  = BASE_DIR / "bin"
INI_FILE = BASE_DIR / "my.ini"

DEFAULT_CFG = {"port": 3306, "pwd": "root"}

def load_cfg():
    if not Path(CFG_FILE).exists():
        Path(CFG_FILE).write_text(json.dumps(DEFAULT_CFG, indent=2), encoding="utf-8")
    return json.loads(Path(CFG_FILE).read_text(encoding="utf-8"))

def save_cfg(c):
    Path(CFG_FILE).write_text(json.dumps(c, indent=2), encoding="utf-8")

cfg = load_cfg()

# ---------- 工具 ----------
def run(cmd, cwd=None):
    cp = subprocess.run(
        cmd, shell=True, cwd=cwd, capture_output=True, text=False,
        creationflags=subprocess.CREATE_NO_WINDOW
    )
    cp.stdout = cp.stdout.decode("gbk", errors="ignore")
    return cp

def process_exists(name: str) -> bool:
    return name.lower() in run(f'tasklist /FI "IMAGENAME eq {name}"').stdout.lower()

# ---------- MySQL ----------
def write_my_ini():
    INI_FILE.write_text(
        f"[mysqld]\nbasedir={BASE_DIR}\ndatadir={DATA_DIR}\nport={cfg['port']}\n"
        f"character-set-server=utf8mb4\ndefault-storage-engine=INNODB\n",
        encoding="utf-8"
    )

def init_mysql():
    if DATA_DIR.exists():
        tray_icon.notify("data 已存在，跳过初始化"); return
    DATA_DIR.mkdir(parents=True, exist_ok=True)
    cmd = f'"{BIN_DIR}/mysqld" --initialize-insecure --basedir="{BASE_DIR}" --datadir="{DATA_DIR}"'
    run(cmd)
    write_my_ini()
    tray_icon.notify("初始化完成，账号 root 密码空")

def is_mysql_running():
    return process_exists("mysqld.exe")



# ---------- Win32 结构体 ----------
class SECURITY_ATTRIBUTES(ctypes.Structure):
    _fields_ = [("nLength", ctypes.c_uint32),
                ("lpSecurityDescriptor", ctypes.c_void_p),
                ("bInheritHandle", ctypes.c_bool)]

class STARTUPINFOW(ctypes.Structure):
    _fields_ = [("cb", ctypes.c_uint32),
                ("lpReserved", ctypes.c_wchar_p),
                ("lpDesktop", ctypes.c_wchar_p),
                ("lpTitle", ctypes.c_wchar_p),
                ("dwX", ctypes.c_uint32),
                ("dwY", ctypes.c_uint32),
                ("dwXSize", ctypes.c_uint32),
                ("dwYSize", ctypes.c_uint32),
                ("dwXCountChars", ctypes.c_uint32),
                ("dwYCountChars", ctypes.c_uint32),
                ("dwFillAttribute", ctypes.c_uint32),
                ("dwFlags", ctypes.c_uint32),
                ("wShowWindow", ctypes.c_uint16),
                ("cbReserved2", ctypes.c_uint16),
                ("lpReserved2", ctypes.c_void_p),
                ("hStdInput", ctypes.c_void_p),
                ("hStdOutput", ctypes.c_void_p),
                ("hStdError", ctypes.c_void_p)]

class PROCESS_INFORMATION(ctypes.Structure):
    _fields_ = [("hProcess", ctypes.c_void_p),
                ("hThread", ctypes.c_void_p),
                ("dwProcessId", ctypes.c_uint32),
                ("dwThreadId", ctypes.c_uint32)]

# ---------- 启动 ----------
def start_mysql(icon=None):
    if is_mysql_running():
        tray_icon.notify("已在运行"); return
    if not DATA_DIR.exists():
        init_mysql()
    write_my_ini()

    exe = str(BIN_DIR / "mysqld.exe")
    cmd = f'"{exe}" --defaults-file="{INI_FILE}"'
    # 初始化结构体
    sa = SECURITY_ATTRIBUTES()
    sa.nLength = ctypes.sizeof(SECURITY_ATTRIBUTES)
    sa.lpSecurityDescriptor = 0
    sa.bInheritHandle = False

    si = STARTUPINFOW()
    si.cb = ctypes.sizeof(STARTUPINFOW)
    si.dwFlags = 0x00000001  # STARTF_USESHOWWINDOW
    si.wShowWindow = 0       # SW_HIDE

    pi = PROCESS_INFORMATION()

    ret = ctypes.windll.kernel32.CreateProcessW(
        None,                       # lpApplicationName
        cmd,                        # lpCommandLine
        ctypes.byref(sa),           # lpProcessAttributes
        ctypes.byref(sa),           # lpThreadAttributes
        False,                      # bInheritHandles
        0x08000000,                 # CREATE_NO_WINDOW
        None,                       # lpEnvironment
        str(BIN_DIR),               # lpCurrentDirectory
        ctypes.byref(si),           # lpStartupInfo
        ctypes.byref(pi)            # lpProcessInformation
    )

    if ret == 0:
        tray_icon.notify(f"启动失败，错误码 {ctypes.get_last_error()}")
    else:
        time.sleep(1.5)
        update_icon()

import psutil   # pip install psutil

def stop_mysql(icon=None):
    # 先优雅关
    cmd = [
        str(BIN_DIR / "mysqladmin.exe"),
        "-u", "root",
        f"-p{cfg['pwd']}",
        "-P", str(cfg["port"]),
        "shutdown"
    ]
    try:
        subprocess.run(cmd, timeout=5, creationflags=subprocess.CREATE_NO_WINDOW)
    except subprocess.TimeoutExpired:
        pass

    # 兜底：强行杀 mysqld.exe
    for p in psutil.process_iter(["pid", "name"]):
        if p.info["name"].lower() == "mysqld.exe":
            try:
                p.terminate()
                p.wait(timeout=3)
            except psutil.TimeoutExpired:
                p.kill()

    time.sleep(0.5)
    update_icon()



# ---------- 密码/端口（无 tkinter） ----------
def change_pwd(icon):
    """调用系统自带的 clip+msg 让用户输入"""
    import tempfile, ctypes
    bat = tempfile.NamedTemporaryFile(delete=False, suffix=".bat", mode="w", encoding="gbk")
    bat.write(f"""
@echo off
set /p pwd=请输入新密码:
echo {cfg["port"]} > "{CFG_FILE}.tmp"
echo|set /p="{cfg["pwd"]}" > nul
"{BIN_DIR}/mysql.exe" -u root -p{cfg["pwd"]} -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '%pwd%'; FLUSH PRIVILEGES;"
del "{CFG_FILE}.tmp"
""")
    bat.close()
    subprocess.Popen([bat.name], creationflags=subprocess.CREATE_NO_WINDOW)
    tray_icon.notify("请在弹出的输入框里填写新密码，回车即可")

def change_port(icon):
    bat = tempfile.NamedTemporaryFile(delete=False, suffix=".bat", mode="w", encoding="gbk")
    bat.write(f"""
@echo off
set /p port=请输入新端口:
powershell -Command "(gc '{CFG_FILE}') -replace '\"port\":.*', '\"port\": %port%,' | sc '{CFG_FILE}'"
""")
    bat.close()
    subprocess.Popen([bat.name], creationflags=subprocess.CREATE_NO_WINDOW)
    tray_icon.notify("端口已保存，重启 MySQL 生效")

# ---------- 托盘 ----------
RUN_IMG  = Image.open(resource_path("res/run.png"))
STOP_IMG = Image.open(resource_path("res/stop.png"))
tray_icon = None

def build_menu():
    return pystray.Menu(
        pystray.MenuItem("初始化", init_mysql),
        pystray.Menu.SEPARATOR,
        pystray.MenuItem("启动",  start_mysql, enabled=lambda _: not is_mysql_running()),
        pystray.MenuItem("停止",  stop_mysql, enabled=lambda _: is_mysql_running()),
        pystray.Menu.SEPARATOR,
        pystray.MenuItem("修改密码", change_pwd),
        pystray.MenuItem("修改端口", change_port),
        pystray.Menu.SEPARATOR,
        pystray.MenuItem("退出", on_exit)
    )
# ---------- 1. 全局先存好两张图标 ----------
RUN_ICON  = Image.open(resource_path("res/run.png"))
STOP_ICON = Image.open(resource_path("res/stop.png"))

# ---------- 2. update_icon() 仅切换图标，不再重复给 .icon 赋值 ----------
def update_icon():
    if is_mysql_running():
        tray_icon.icon = RUN_ICON
        tray_icon.title = "MySQL 运行中"
    else:
        tray_icon.icon = STOP_ICON
        tray_icon.title = "MySQL 已停止"
    tray_icon.menu = build_menu()

def on_exit(icon, item):
    stop_mysql() 
    icon.stop()

# ---------- 3. 启动托盘时一次性指定 STOP_ICON ----------
def setup_tray():
    global tray_icon
    tray_icon = pystray.Icon(
        "MySQL_Manager",
        icon=STOP_ICON,      # 先给一张图
        title="MySQL 已停止",
        menu=build_menu()
    )
    threading.Thread(
        target=lambda: [time.sleep(1) or update_icon() for _ in iter(int, 1)],
        daemon=True
    ).start()
    tray_icon.run()
    
def main():
    global tray_icon
    tray_icon = pystray.Icon("MySQL_Manager", STOP_IMG, "MySQL 已停止", build_menu())

    if not DATA_DIR.exists():                      # 尚未初始化
        subprocess.run(
            f'msg %USERNAME% /TIME:10 "MySQL 还未初始化，请先在托盘菜单点击【初始化】"',
            shell=True,
            creationflags=subprocess.CREATE_NO_WINDOW
        )
    else:                                          # 已初始化，直接启动
        start_mysql()

    # 启动托盘
    update_icon()
    threading.Thread(target=lambda: [time.sleep(1) or update_icon()
                                     for _ in iter(int, 1)], daemon=True).start()
    tray_icon.run()

if __name__ == "__main__":
    main()